#pragma once

#include "../Driver/DriverModel.h"

namespace Axis {
namespace sml = boost::sml;

// event
struct TickEvent {};
struct PowerOnEvent {};
struct PowerOffEvent{};
struct MoveEvent {};
struct HomeEvent {};
struct FasterEvent {};
struct SlowerEvent {};
struct StopEvent {};
struct CancelHomeEvent {};
struct ResetEvent {};

struct NormalState {
    // state
    struct PowerOnState{};
    struct MovingState{};
    struct HomingState{};
    auto operator()() const noexcept {
        using namespace sml;
        // state
        auto power_on_s = state<PowerOnState>;
        auto moving_s = state<MovingState>;
        auto homing_s = state<HomingState>;
        // event
        auto tick_e = event<TickEvent>;
        auto move_e = event<MoveEvent>;
        auto home_e = event<HomeEvent>;
        auto faster_e = event<FasterEvent>;
        auto slower_e = event<SlowerEvent>;
        auto stop_e = event<StopEvent>;
        auto cancel_home_e = event<CancelHomeEvent>;
        // guard
        const auto moving_g = [](Driver::DriverModel& driver) { return driver.IsMoving(); };
        const auto stop_g = [](Driver::DriverModel& driver) { return driver.IsStandStill(); };
        const auto homing_g = [](Driver::DriverModel& driver) { return driver.IsHoming(); };
        const auto exit_home_g = [](Driver::DriverModel& driver) {
            return driver.IsHomeFinished() || driver.IsHomeErrored() || driver.IsHomeHalted();
        };
        // action
        auto entry_power_on_a = []() { std::cout << "Entry PowerOnState" << std::endl; };
        auto move_a = [](Driver::DriverModel& driver) { driver.Move(); };
        auto home_a = [](Driver::DriverModel& driver) { driver.Home(); };
        auto faster_a = [](Driver::DriverModel& driver) { driver.Faster(); };
        auto slower_a = [](Driver::DriverModel& driver) { driver.Slower(); };
        auto stop_a = [](Driver::DriverModel& driver) { driver.Stop(); };
        auto cancel_home_a = [](Driver::DriverModel& driver) {
            driver.HaltHome();
            // TODO 修复暂停
        };
        return make_transition_table(
            *power_on_s + sml::on_entry<_> / entry_power_on_a,
            power_on_s  + tick_e [moving_g]       = moving_s,
            power_on_s  + tick_e [homing_g]       = homing_s,
            power_on_s  + move_e / move_a,
            power_on_s  + home_e / home_a,
            moving_s    + faster_e / faster_a,
            moving_s    + slower_e / slower_a,
            moving_s    + stop_e / stop_a,
            moving_s    + tick_e [stop_g]         = power_on_s,
            homing_s    + cancel_home_e / cancel_home_a,
            homing_s    + tick_e [exit_home_g]    = power_on_s
        );
    };

};

struct AxisStateMachine {
    // state
    struct InitState{};
    struct PrePowerOn{};
    struct PrePowerOff{};
    struct FaultState{};

    auto operator()() const noexcept {
        using namespace sml;
        // state
        auto init_s = state<InitState>;
        auto pre_power_on_s = state<PrePowerOn>;
        auto pre_power_off_s = state<PrePowerOff>;
        auto normal_s = state<NormalState>;
        auto fault_s = state<FaultState>;
        // event
        auto power_on_e = event<PowerOnEvent>;
        auto power_off_e = event<PowerOffEvent>;
        auto reset_e = event<ResetEvent>;
        auto tick_e = event<TickEvent>;
        // guard
        const auto powered_on_g = [](Driver::DriverModel& driver) { return driver.IsOperationEnabled(); };
        const auto powered_off_g = [](Driver::DriverModel& driver) {return driver.IsSwitchOnDisabled(); };
        const auto error_g = [](Driver::DriverModel& driver) {return driver.IsFault(); };
        const auto reset_g = [](Driver::DriverModel& driver) {return driver.IsNormal(); };
        // action
        const auto power_on_a = [](Driver::DriverModel& driver) {
            if (driver.IsSwitchOnDisabled()) {
                driver.ShutDown();
            } else if (driver.IsReadyToSwitchOn()) {
                driver.SwitchOn();
            } else if (driver.IsSwitchedOn()) {
                std::cout << "IsSwitchedOn" << std::endl;
                driver.EnableOperation();
            } else {
                std::cout << "OtherState" << std::endl;
            }
        };
        const auto power_off_a = [](Driver::DriverModel& driver) {
            if (driver.IsOperationEnabled()) {
                driver.DisableOperation();
            } else if (driver.IsSwitchedOn()) {
                driver.ShutDown();
            } else if (driver.IsReadyToSwitchOn()) {
                driver.DisableVoltage();
            }
        };
        const auto reset_a = []() { std::cout << "Reset" << std::endl; };
        return make_transition_table(
            *init_s         + sml::on_entry<_> / [](){ std::cout << "Entry init_s state" << std::endl; },
            pre_power_on_s  + sml::on_entry<_> / [](){ std::cout << "Entry pre_power_on state" << std::endl; },
            normal_s        + sml::on_entry<_> / [](){ std::cout << "Entry normal_s state" << std::endl; },
            pre_power_off_s + sml::on_entry<_> / [](){ std::cout << "Entry pre_power_off_s state" << std::endl; },
            fault_s         + sml::on_entry<_> / [](){ std::cout << "Entry fault_s state" << std::endl; },
            init_s          + power_on_e                = pre_power_on_s,
            init_s          + tick_e [error_g]          = fault_s,
            pre_power_on_s  + tick_e / power_on_a,
            pre_power_on_s  + tick_e [powered_on_g]     = normal_s,
            pre_power_on_s  + tick_e [error_g]          = fault_s,
            normal_s        + power_off_e               = pre_power_off_s,
            normal_s        + tick_e [error_g]          = fault_s,
            pre_power_off_s + tick_e / power_off_a,
            pre_power_off_s + tick_e [powered_off_g]    = init_s,
            fault_s         + tick_e [reset_g]          = init_s,
            fault_s         + reset_e / reset_a
        );
    };
};
}


